'use strict';

/** 
 * 发送请求
 * @param {string} url 请求的URL
 * @param {object} options 如下
 *        @param {function} start loading开始执行函数
 *        @param {function} finsh loading执行结束函数
 *        @param {function} success 处理成功之后对值的处理函数
 *        @param {function} error 请求出错时的处理函数
 *        @param {string} dataType res值的解析方式
 *        @param {object} options 传递给请求的数据，包括请求头
 * 
 **/ 
async function ajax(url, options){
    if ('string' !== typeof url) {
        throw new Error('[Fetch Error] `url` must be a string !');
    }
    var start = 'function' === typeof options.start ? options.start : Fetch.basicSetup.start;
    var finsh = 'function' === typeof options.finsh ? options.finsh : Fetch.basicSetup.finsh;
    var success = 'function' === typeof options.success ? options.success : Fetch.basicSetup.success;
    var error = 'function' === typeof options.error ? options.error : Fetch.basicSetup.error;
    var dataType = 'string' === typeof options.dataType ? options.dataType : 'json';
    var status = Object.assign({}, Fetch.basicSetup.status, options.status);

    try {
        delete options.start;
        delete options.finsh;
        delete options.success;
        delete options.error;
        delete options.dataType;
        delete options.status;
    } catch (error) {
        options.start = undefined;
        options.finsh = undefined;
        options.success = undefined;
        options.error = undefined;
        options.dataType = undefined;
        options.status = undefined;
    }

    if (options.headers) {
        options.headers = new Headers(options.headers);
    }

    start();
    const userData = await fetch(url, options).then(res=>{

        if (res.status >= 400) {
            var msg = status[10002].replace('{status}', res.status).replace('{statusText}',res.statusText);
            return new Error(msg);
        }

        if (res[dataType]) {
            return res[dataType]();
        }

        return new Error(status[10001].replace('{dataType}', dataType));
    }).then(data=> {
        if (data instanceof Error) {
            error(data);
            return ;
        }
        return success(data);
    }).catch(err=>{
        error(err);
    });

    finsh(); 
    return userData;
}

export class Fetch { 
    /**
     * Fetch Get 请求
     * 
     * @param {string} url 
     * @param {object} options
     */
    async get(url, options){
        !ajax.configurable ? this.configure() : ajax.configurable = 1;
        options = 'object' !== typeof options ? {} : options;
        options.method = 'GET';

        var param = options.body;
        if (param) {
            if (!self.URLSearchParams) {
                throw new Error('No object `URLSearchParams` was found.')
            } 
            
            var search = new URLSearchParams(param).toString();

            if (url.search(/\?/) === -1) {  
                url += '?' + search; 
            } else {  
                url += '&' + search;
            } 

            try {
                delete options.body;
            } catch (error) {
                options.body = undefined;
            }
        }

        return await ajax(url, options);
    }

    /** 
     * Fetch Post 
     * 
     **/ 
    async post (url, options){
        !ajax.configurable ? this.configure() : ajax.configurable = 1;
        options = 'object' !== typeof options ? {} : options;
        options.method = 'POST';
        options.body = JSON.stringify(options.body);

        return await ajax(url, options);
    }

    // Global Configure
    configure (options){
        options = 'object' !== typeof options ? {} : options;
        
        var noop = function(){};
        var returned = function (res){ return res}; 
        var basicSetup = Fetch.basicSetup;

        basicSetup.start = 'function' === typeof options.start ? options.start : noop;
        basicSetup.finsh = 'function' === typeof options.finsh ? options.finsh : noop;
        basicSetup.success = 'function' === typeof options.success ? options.success : returned;
        basicSetup.error = 'function' === typeof options.error ? options.error : noop;
        basicSetup.options = Object.assign({},basicSetup.options, options.options);
        basicSetup.status = Object.assign({},basicSetup.status, options.status);
    }
};

// 全局公用的, 所有请求共享，可自定义
Fetch.basicSetup = {
    // 请求过程中的loading动画
    start: null, // loading的启动函数
    finsh: null, // 请求处理完成之后
    // 请求成功后的公用处理函数
    success: null,
    // 请求出错时的处理函数
    error: null, 
    // 默认的请求配置
    options: { 
        // 请求时携带cookie
        credentials: false,
        // 请求头配置
        headers: null
    },
    // 状态码
    status: {
        10001: 'fetch不存在相应的值解析方式，你当前的解析方式是{dataType}',
        10002: '服务端处理失败，返回的状态码是{status}，状态描述{statusText}'
    }
};